/**
 * @fileOverview Bexf - Browser Extension Framework
 *
 * @author Mikhail Davydov <azazel.production@gmail.com>
 * 
 * @licence MIT
 */

/**#nocode+*/
(function (exports, global, exportAs) {
/**#nocode-*/

    /**#nocode+*/
    var go = global.opera, gw = global.widget, goe, goc, goct,
    /**#nocode-*/

    /**
     * @namespace
     * @ignore
     */
    possibleAttrs = {
        disabled: 1,
        icon: 1,
        popup: 0,
        badge: 0,
        title: 1
    },

    /**
     * @namespace
     * @ignore
     */
    possibleBadgeAttrs = {
        backgroundColor: 1,
        color: 1,
        display: 1,
        textContent: 1
    },

    /**
     * @namespace
     * @ignore
     */
    possiblePopupAttrs = {
        href: 1,
        width: 1,
        height: 1
    },

    /**
     * @namespace
     * @ignore
     */
    possibleTabsAttrs = {
        focused: 1,
        url: 1
    },

    /**
     * @namespace
     * @ignore
     */
    possibleWindowsAttrs = {
        focused: 1,
        tabs: 1
    },


    /**
     * @namespace
     * @ignore
     */
    Classes = {},
    Utils, Xhr, CommonObject, Window, Tab, Button, BexfFactory, item;

    if (typeof go !== 'undefined') {
        goe = go.extension;
        goc = go.contexts;
        if (typeof goc !== 'undefined') {
            goct = goc.toolbar;
        }
    }

    /**
     * @namespace
     */
    Utils = {
        /**
         * Default event method
         *
         * @param {String}   eventName
         * @param {Function} eventListener
         *
         * @returns {Mixed}
         */
        defaultEvent: function (eventName, eventListener) {
            if (typeof eventListener === 'function') {
                return this.addEventListener(eventName, eventListener, false);
            } else {
                if (typeof this[eventName] === 'function') {
                    this[eventName]();
                }
            }
            return true;
        },
        /**
         * Inherit function
         *
         * @param {Object} child
         * @param {Object} parent
         *
         * @returns {Object}
         */
        inherit: function (child, parent) {
            function F() {}
            F.prototype = parent.prototype;
            child.prototype = new F();
            child.prototype.constructor = child;
            child.superproto = parent.prototype;
            return child;
        },
        /**
         * Adds events to object
         *
         * @param {Object}   object
         * @param {String[]} events
         */
        addEvents: function (object, events) {
            for (var i = 0, c = events.length; i < c; i += 1) {
                object.prototype[events[i]] = (function (item) {
                    return function (eventListener) {
                        Utils.defaultEvent.call(this.context, item, eventListener);
                        return this;
                    }
                } (events[i]));
            }
        },
        /**
         * Adds props to object
         *
         * @param {Object}   object
         * @param {String[]} props
         */
        addProps: function (object, props) {
            for (var i = 0, c = props.length; i < c; i += 1) {
                object.prototype[props[i]] = (function (item) {
                    return function (value) {
                        return this._setAttr(item, value);
                    }
                } (props[i]));
            }
        }
    };

    /**
     * @constructor Xhr
     */
    Xhr = function () {};

    /**
     * Performs XHR request
     *
     * @param {Object} params
     */
    Xhr.prototype.request = function(params) {
        params.method = params.method.toUpperCase() || 'GET';
        params.url = params.url || '';
        params.postVars = params.method !== 'POST' ? (params.postVars || null) : null;
        params.success = params.success || function () {};
        params.error = params.error || function () {};

        var xhr = new XMLHttpRequest(), self = this;
        xhr.onreadystatechange = function() {
            try {
                if (xhr.readyState !== 4) {
                    return;
                }
                self.processResponse(params, {
                    status: xhr.status,
                    responseText: xhr.responseText,
                    contentType: xhr.getResponseHeader('Content-type')
                });
            } catch (e) {
                params.error(xhr.status);
            }
        };
        xhr.open(params.method, params.url, true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.send(params.postVars);
    };
    /**
     * Processes request
     *
     * @param {Object} params
     * @param {Object} xhr
     *
     * @returns {Mixed}
     */
    Xhr.prototype.processResponse = function (params, xhr) {
        if (xhr.status === 200) {
            if (xhr.contentType.match(/^application\/json/)) {
                params.success(JSON.parse(xhr.responseText));
            } else {
                params.success(xhr.responseText);
            }
        } else {
            params.error(xhr.status);
        }
    };

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /**
     * @constructor CommonObject
     */
    CommonObject = function () {};

    /**
     * Default bind method
     *
     * @param {String}   type
     * @param {Function} eventListener
     * @param {Boolean}  [useCapture=false]
     *
     * @returns {CommonObject}
     */
    CommonObject.prototype.bind = function (type, eventListener, useCapture) {
        this.context.addEventListener(type, eventListener, useCapture);
        return this;
    }

    /**
     * Default unbind method
     *
     * @param {String}   type
     * @param {Function} eventListener
     * @param {Boolean}  [useCapture=false]
     *
     * @returns {CommonObject}
     */
    CommonObject.prototype.unbind = function (type, eventListener, useCapture) {
        this.context.removeEventListener(type, eventListener, useCapture);
        return this;
    }

    /**
     * Default attr method
     *
     * @param {String} name
     * @param {Mixed}  value
     *
     * @returns {Mixed}
     */
    CommonObject.prototype.attr = function (name, value) {
        if (typeof name === 'object') {
            for (var index in name) {
                if (name.hasOwnProperty(index)) {
                    this._setAttr(index, name[index]);
                }
            }
            return this;
        } else {
            return this._setAttr(name, value);
        }
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /**
     * @constructor Button
     *
     * @param {Object} context
     *
     * @augments CommonObject
     */
    Button = function (context) {
        context.popup = context.popup || {};
        context.badge = context.badge || {};
        context = goct.createItem(context);
        this.context = context;
    }

    Utils.inherit(Button, CommonObject);


    /**
     * @name Button.remove
     * @event
     */

    /**
     * @name Button.click
     * @event
     */
    Utils.addEvents(Button, ['remove', 'click']);

    Button.prototype._setAttr = function (name, value) {
        if (value && typeof possibleAttrs[name] !== 'undefined') {
            if (possibleAttrs[name]) {
                this.context[name] = value;
            } else {
                return this.context[name];
            }
        } else if (value && typeof possibleBadgeAttrs[name] !== 'undefined') {
            if (possibleBadgeAttrs[name]) {
                this.context.badge[name] = value;
            } else {
                return this.context.badge[name];
            }
        } else if (value && typeof possiblePopupAttrs[name] !== 'undefined') {
            if (possiblePopupAttrs[name]) {
                this.context.popup[name] = value;
            } else {
                return this.context.popup[name];
            }
        }
        return this;
    }

    /**
     * @name Button.title
     * @function
     *
     * @param {String} name
     * @param {Mixed} value
     *
     * @returns {Mixed}
     */

    /**
     * @name Button.href
     * @function
     *
     * @param {String} name
     * @param {Mixed} value
     *
     * @returns {Mixed}
     */
    
    /**
     * @name Button.icon
     * @function
     * 
     * @param {String} name
     * @param {Mixed} value
     *
     * @returns {Mixed}
     */
    Utils.addProps(Button, ['title', 'href', 'icon']);

    /**
     * @param {String} text
     *
     * @returns {Mixed}
     */
    Button.prototype.text = function (text) {
        return this._setAttr('textContent', text);
    }

    /**
     * @returns {Mixed}
     */
    Button.prototype.hideBadge = function () {
        return this._setAttr('display', 'none');
    }

    /**
     * @returns {Mixed}
     */
    Button.prototype.showBadge = function () {
        return this._setAttr('display', 'block');
    }

    /**
     * @returns {Mixed}
     */
    Button.prototype.disable = function () {
        return this._setAttr('disabled', true);
    }

    /**
     * @returns {Mixed}
     */
    Button.prototype.enable = function () {
        return this._setAttr('disabled', false);
    }

    /**
     * Adds button to panel
     *
     * @returns {Button}
     */
    Button.prototype.addToPanel = function () {
        BexfFactory.addButton(this);
        return this;
    }

    /**
     * @returns {Button}
     */
    Button.prototype.get = function () {
        return this;
    }

    /**
     * Removes button from panel
     *
     * @returns {Button}
     */
    Button.prototype.removeFromPanel = function () {
        BexfFactory.removeButton(this);
        return this;
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /**
     * @constructor Tab
     *
     * @param {Object} context
     *
     * @augments CommonObject
     */
    Tab = function (context) {
        context = goe.tabs.create(context);
        this.context = context;
    }

    Utils.inherit(Tab, CommonObject);

    /**
     * @name Button.close
     * @event
     */

    /**
     * @name Button.blur
     * @event
     */

    /**
     * @name Button.create
     * @event
     */

    /**
     * @name Button.focus
     * @event
     */
    Utils.addEvents(Tab, ['close', 'blur', 'create', 'focus']);

    Tab.prototype._setAttr = function (name, value) {
        if (value && typeof possibleTabsAttrs[name] !== 'undefined') {
            if (possibleTabsAttrs[name]) {
                this.context[name] = value;
            } else {
                return this.context[name];
            }
        }
        return this;
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /**
     * @constructor Tab
     *
     * @param {Object} context
     *
     * @augments Tab
     */
    Window = function (context) {
        context = goe.tabs.create(context);
        this.context = context;
    }

    Utils.inherit(Window, Tab);

    Window.prototype._setAttr = function (name, value) {
        if (value && typeof possibleWindowsAttrs[name] !== 'undefined') {
            if (possibleWindowsAttrs[name]) {
                this.context[name] = value;
            } else {
                return this.context[name];
            }
        }
        return this;
    }

    Classes.Window = Window;
    Classes.Tab = Tab;
    Classes.Button = Button;

    /**
     * Bexf Factory
     * 
     * @namespace
     * @param {Mixed} ctx
     *
     * @returns {Mixed}
     */
    BexfFactory = function (ctx) {
        return ctx;
    }

    /**
     * @name BexfFactory.createWindow
     * @function
     * 
     * @param {Object} options
     *
     * @returns {Window}
     */

    /**
     * @name BexfFactory.createTab
     * @function
     *
     * @param {Object} options
     *
     * @returns {Tab}
     */

    /**
     * @name BexfFactory.createButton
     * @function
     * 
     * @param {Object} options
     *
     * @returns {Button}
     */

    for (item in Classes) {
        BexfFactory['create' + item] = (function (item) {
            return function (options) {
                if (options instanceof Classes[item]) {
                    return options;
                } else {
                    return new Classes[item](options);
                }
            };
        } (item));
    }

    /**
     * Adds button
     * 
     * @param {Button} button
     *
     * @returns {BexfFactory}
     */
    BexfFactory.addButton = function (button) {
        if (button instanceof Button) {
            button = button.context;
        }
        goct.addItem(button);
        return this;
    }

    /**
     * Removes button
     *
     * @param {Button} button
     *
     * @returns {BexfFactory}
     */
    BexfFactory.removeButton = function (button) {
        if (button instanceof Button) {
            button = button.context;
        }
        goct.removeItem(button);
        return this;
    }

    /**
     * Gets button by index
     *
     * @param {Number} index
     *
     * @returns {BexfFactory}
     */
    BexfFactory.getButton = function (index) {
        var element = goct.item(index);
        if (element instanceof global.ToolbarUIItem) {
            return new Button(element);
        }
        return null;
    }

    /**
     * Buttons count
     *
     * @returns {Number}
     */
    BexfFactory.length = function () {
        return goct.length();
    }

    /**
     * Attr getter
     *
     * @param {String} name
     *
     * @returns {Mixed}
     */
    BexfFactory.attr = function (name) {
        if (gw.hasOwnProperty(name)) {
            return gw[name];
        }
        return null;
    }

    /**
     * Widget preferences
     *
     * @param {String} name
     * @param {Mixed}  value
     *
     * @returns {Mixed}
     */
    BexfFactory.opt = function (name, value) {
        if (!name) {
            return gw.preferences;
        } else if (typeof value === 'undefined') {
            return gw.preferences.getItem(name)
        } else {
            gw.preferences.setItem(name, value);
            return this;
        }
    }

    /**
     * @event
     */
    BexfFactory.connect = function (eventListener) {
        Utils.defaultEvent.call(goe, 'connect', eventListener);
        return this;
    }

    /**
     * @event
     */
    BexfFactory.disconnect = function (eventListener) {
        Utils.defaultEvent.call(typeof go !== 'undefined' ? goe : global, 'disconnect', eventListener);
        return this;
    }

    /**
     * @event
     */
    BexfFactory.ready = function (eventListener) {
        Utils.defaultEvent.call(global, 'load', eventListener);
        return this;
    }

    /**
     * @event
     */
    BexfFactory.message = function (eventListener) {
        Utils.defaultEvent.call(typeof go !== 'undefined' ? goe : global, 'message', eventListener);
        return this;
    }

    /**
     * bind method
     *
     * @param {String}   type
     * @param {Function} eventListener
     * @param {Boolean}  [useCapture=false]
     *
     * @returns {CommonObject}
     */
    BexfFactory.bind = function (type, eventListener, useCapture) {
        goe.addEventListener(type, eventListener, useCapture);
        return this;
    }

    /**
     * unbind method
     *
     * @param {String}   type
     * @param {Function} eventListener
     * @param {Boolean}  [useCapture=false]
     *
     * @returns {CommonObject}
     */
    BexfFactory.unbind = function (type, eventListener, useCapture) {
        goe.removeEventListener(type, eventListener, useCapture);
        return this;
    }

    /**
     * Broadcasts message
     *
     * @param {Mixed} message
     *
     * @returns {BexfFactory}
     */
    BexfFactory.postMessage = function (message) {
        if (typeof go !== 'undefined') {
            goe.broadcastMessage(message);
        } else {
            global.postMessage(message);
        }
        return this;
    }

    /**
     * @returns {Object}
     */
    BexfFactory.tabs = function () {
        return goe.tabs;
    }

    /**
     * @returns {Object}
     */
    BexfFactory.windows = function () {
        return goe.windows;
    }

    /**
     * Simple ajax
     *
     * @param {Object} params
     */
    BexfFactory.ajax = function (params) {
        var xhr = new Xhr();
        xhr.request(params);
    }

    exports[exportAs] = BexfFactory;

/**#nocode+*/
}(this, this, '$'));
/**#nocode-*/